/*
 * Copyright (C) 2005-2011 MaNGOS <http://www.getmangos.com/>
 *
 * Copyright (C) 2008-2011 Trinity <http://www.trinitycore.org/>
 *
 * Copyright (C) 2010-2011 ArkCORE <http://www.arkania.net/>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#ifndef ARKCORECORE_GAMEOBJECT_H
#define ARKCORECORE_GAMEOBJECT_H

#include "Common.h"
#include "SharedDefines.h"
#include "Unit.h"
#include "Object.h"
#include "LootMgr.h"
#include "DatabaseEnv.h"

class GameObjectAI;

// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif

#define MAX_GAMEOBJECT_QUEST_ITEMS 6

// from `gameobject_template`
struct GameObjectInfo {
	uint32 id;
	uint32 type;
	uint32 displayId;
	char *name;
	char *IconName;
	char *castBarCaption;
	char *unk1;
	uint32 faction;
	uint32 flags;
	float size;
	uint32 questItems[MAX_GAMEOBJECT_QUEST_ITEMS];
	union // different GO types have different data field
	{
		//0 GAMEOBJECT_TYPE_DOOR
		struct {
			uint32 startOpen; //0 used client side to determine GO_ACTIVATED means open/closed
			uint32 lockId; //1 -> Lock.dbc
			uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
			uint32 noDamageImmune; //3 break opening whenever you recieve damage?
			uint32 openTextID; //4 can be used to replace castBarCaption?
			uint32 closeTextID; //5
			uint32 ignoredByPathing; //6
		} door;
		//1 GAMEOBJECT_TYPE_BUTTON
		struct {
			uint32 startOpen; //0
			uint32 lockId; //1 -> Lock.dbc
			uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
			uint32 linkedTrap; //3
			uint32 noDamageImmune; //4 isBattlegroundObject
			uint32 large; //5
			uint32 openTextID; //6 can be used to replace castBarCaption?
			uint32 closeTextID; //7
			uint32 losOK; //8
		} button;
		//2 GAMEOBJECT_TYPE_QUESTGIVER
		struct {
			uint32 lockId; //0 -> Lock.dbc
			uint32 questList; //1
			uint32 pageMaterial; //2
			uint32 gossipID; //3
			uint32 customAnim; //4
			uint32 noDamageImmune; //5
			uint32 openTextID; //6 can be used to replace castBarCaption?
			uint32 losOK; //7
			uint32 allowMounted; //8
			uint32 large; //9
		} questgiver;
		//3 GAMEOBJECT_TYPE_CHEST
		struct {
			uint32 lockId; //0 -> Lock.dbc
			uint32 lootId; //1
			uint32 chestRestockTime; //2
			uint32 consumable; //3
			uint32 minSuccessOpens; //4
			uint32 maxSuccessOpens; //5
			uint32 eventId; //6 lootedEvent
			uint32 linkedTrapId; //7
			uint32 questId; //8 not used currently but store quest required for GO activation for player
			uint32 level; //9
			uint32 losOK; //10
			uint32 leaveLoot; //11
			uint32 notInCombat; //12
			uint32 logLoot; //13
			uint32 openTextID; //14 can be used to replace castBarCaption?
			uint32 groupLootRules; //15
			uint32 floatingTooltip; //16
		} chest;
		//4 GAMEOBJECT_TYPE_BINDER - empty
		//5 GAMEOBJECT_TYPE_GENERIC
		struct {
			uint32 floatingTooltip; //0
			uint32 highlight; //1
			uint32 serverOnly; //2
			uint32 large; //3
			uint32 floatOnWater; //4
			int32 questID; //5
		} _generic;
		//6 GAMEOBJECT_TYPE_TRAP
		struct {
			uint32 lockId; //0 -> Lock.dbc
			uint32 level; //1
			uint32 radius; //2 radius for trap activation
			uint32 spellId; //3
			uint32 charges; //4 need respawn (if > 0)
			uint32 cooldown; //5 time in secs
			int32 autoCloseTime; //6
			uint32 startDelay; //7
			uint32 serverOnly; //8
			uint32 stealthed; //9
			uint32 large; //10
			uint32 invisible; //11
			uint32 openTextID; //12 can be used to replace castBarCaption?
			uint32 closeTextID; //13
			uint32 ignoreTotems; //14
		} trap;
		//7 GAMEOBJECT_TYPE_CHAIR
		struct {
			uint32 slots; //0
			uint32 height; //1
			uint32 onlyCreatorUse; //2
			uint32 triggeredEvent; //3
		} chair;
		//8 GAMEOBJECT_TYPE_SPELL_FOCUS
		struct {
			uint32 focusId; //0
			uint32 dist; //1
			uint32 linkedTrapId; //2
			uint32 serverOnly; //3
			uint32 questID; //4
			uint32 large; //5
			uint32 floatingTooltip; //6
		} spellFocus;
		//9 GAMEOBJECT_TYPE_TEXT
		struct {
			uint32 pageID; //0
			uint32 language; //1
			uint32 pageMaterial; //2
			uint32 allowMounted; //3
		} text;
		//10 GAMEOBJECT_TYPE_GOOBER
		struct {
			uint32 lockId; //0 -> Lock.dbc
			int32 questId; //1
			uint32 eventId; //2
			uint32 autoCloseTime; //3
			uint32 customAnim; //4
			uint32 consumable; //5
			uint32 cooldown; //6
			uint32 pageId; //7
			uint32 language; //8
			uint32 pageMaterial; //9
			uint32 spellId; //10
			uint32 noDamageImmune; //11
			uint32 linkedTrapId; //12
			uint32 large; //13
			uint32 openTextID; //14 can be used to replace castBarCaption?
			uint32 closeTextID; //15
			uint32 losOK; //16 isBattlegroundObject
			uint32 allowMounted; //17
			uint32 floatingTooltip; //18
			uint32 gossipID; //19
			uint32 WorldStateSetsState; //20
		} goober;
		//11 GAMEOBJECT_TYPE_TRANSPORT
		struct {
			uint32 pause; //0
			uint32 startOpen; //1
			uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
			uint32 pause1EventID; //3
			uint32 pause2EventID; //4
		} transport;
		//12 GAMEOBJECT_TYPE_AREADAMAGE
		struct {
			uint32 lockId; //0
			uint32 radius; //1
			uint32 damageMin; //2
			uint32 damageMax; //3
			uint32 damageSchool; //4
			uint32 autoCloseTime; //5 secs till autoclose = autoCloseTime / 0x10000
			uint32 openTextID; //6
			uint32 closeTextID; //7
		} areadamage;
		//13 GAMEOBJECT_TYPE_CAMERA
		struct {
			uint32 lockId; //0 -> Lock.dbc
			uint32 cinematicId; //1
			uint32 eventID; //2
			uint32 openTextID; //3 can be used to replace castBarCaption?
		} camera;
		//14 GAMEOBJECT_TYPE_MAPOBJECT - empty
		//15 GAMEOBJECT_TYPE_MO_TRANSPORT
		struct {
			uint32 taxiPathId; //0
			uint32 moveSpeed; //1
			uint32 accelRate; //2
			uint32 startEventID; //3
			uint32 stopEventID; //4
			uint32 transportPhysics; //5
			uint32 mapID; //6
			uint32 worldState1; //7
		} moTransport;
		//16 GAMEOBJECT_TYPE_DUELFLAG - empty
		//17 GAMEOBJECT_TYPE_FISHINGNODE - empty
		//18 GAMEOBJECT_TYPE_SUMMONING_RITUAL
		struct {
			uint32 reqParticipants; //0
			uint32 spellId; //1
			uint32 animSpell; //2
			uint32 ritualPersistent; //3
			uint32 casterTargetSpell; //4
			uint32 casterTargetSpellTargets; //5
			uint32 castersGrouped; //6
			uint32 ritualNoTargetCheck; //7
		} summoningRitual;
		//19 GAMEOBJECT_TYPE_MAILBOX - empty
		//20 GAMEOBJECT_TYPE_DONOTUSE - empty
		//21 GAMEOBJECT_TYPE_GUARDPOST
		struct {
			uint32 creatureID; //0
			uint32 charges; //1
		} guardpost;
		//22 GAMEOBJECT_TYPE_SPELLCASTER
		struct {
			uint32 spellId; //0
			uint32 charges; //1
			uint32 partyOnly; //2
			uint32 allowMounted; //3
			uint32 large; //4
		} spellcaster;
		//23 GAMEOBJECT_TYPE_MEETINGSTONE
		struct {
			uint32 minLevel; //0
			uint32 maxLevel; //1
			uint32 areaID; //2
		} meetingstone;
		//24 GAMEOBJECT_TYPE_FLAGSTAND
		struct {
			uint32 lockId; //0
			uint32 pickupSpell; //1
			uint32 radius; //2
			uint32 returnAura; //3
			uint32 returnSpell; //4
			uint32 noDamageImmune; //5
			uint32 openTextID; //6
			uint32 losOK; //7
		} flagstand;
		//25 GAMEOBJECT_TYPE_FISHINGHOLE
		struct {
			uint32 radius; //0 how close bobber must land for sending loot
			uint32 lootId; //1
			uint32 minSuccessOpens; //2
			uint32 maxSuccessOpens; //3
			uint32 lockId; //4 -> Lock.dbc; possibly 1628 for all?
		} fishinghole;
		//26 GAMEOBJECT_TYPE_FLAGDROP
		struct {
			uint32 lockId; //0
			uint32 eventID; //1
			uint32 pickupSpell; //2
			uint32 noDamageImmune; //3
			uint32 openTextID; //4
		} flagdrop;
		//27 GAMEOBJECT_TYPE_MINI_GAME
		struct {
			uint32 gameType; //0
		} miniGame;
		//29 GAMEOBJECT_TYPE_CAPTURE_POINT
		struct {
			uint32 radius; //0
			uint32 spell; //1
			uint32 worldState1; //2
			uint32 worldstate2; //3
			uint32 winEventID1; //4
			uint32 winEventID2; //5
			uint32 contestedEventID1; //6
			uint32 contestedEventID2; //7
			uint32 progressEventID1; //8
			uint32 progressEventID2; //9
			uint32 neutralEventID1; //10
			uint32 neutralEventID2; //11
			uint32 neutralPercent; //12
			uint32 worldstate3; //13
			uint32 minSuperiority; //14
			uint32 maxSuperiority; //15
			uint32 minTime; //16
			uint32 maxTime; //17
			uint32 large; //18
			uint32 highlight; //19
			uint32 startingValue; //20
			uint32 unidirectional; //21
		} capturePoint;
		//30 GAMEOBJECT_TYPE_AURA_GENERATOR
		struct {
			uint32 startOpen; //0
			uint32 radius; //1
			uint32 auraID1; //2
			uint32 conditionID1; //3
			uint32 auraID2; //4
			uint32 conditionID2; //5
			uint32 serverOnly; //6
		} auraGenerator;
		//31 GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY
		struct {
			uint32 mapID; //0
			uint32 difficulty; //1
		} dungeonDifficulty;
		//32 GAMEOBJECT_TYPE_BARBER_CHAIR
		struct {
			uint32 chairheight; //0
			uint32 heightOffset; //1
		} barberChair;
		//33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
		struct {
			uint32 intactNumHits; //0
			uint32 creditProxyCreature; //1
			uint32 state1Name; //2
			uint32 intactEvent; //3
			uint32 damagedDisplayId; //4
			uint32 damagedNumHits; //5
			uint32 empty3; //6
			uint32 empty4; //7
			uint32 empty5; //8
			uint32 damagedEvent; //9
			uint32 destroyedDisplayId; //10
			uint32 empty7; //11
			uint32 empty8; //12
			uint32 empty9; //13
			uint32 destroyedEvent; //14
			uint32 empty10; //15
			uint32 debuildingTimeSecs; //16
			uint32 empty11; //17
			uint32 destructibleData; //18
			uint32 rebuildingEvent; //19
			uint32 empty12; //20
			uint32 empty13; //21
			uint32 damageEvent; //22
			uint32 empty14; //23
		} building;
		//34 GAMEOBJECT_TYPE_GUILDBANK - empty
		//35 GAMEOBJECT_TYPE_TRAPDOOR
		struct {
			uint32 whenToPause; // 0
			uint32 startOpen; // 1
			uint32 autoClose; // 2
		} trapDoor;

		// not use for specific field access (only for output with loop by all filed), also this determinate max union size
		struct {
			uint32 data[32];
		} raw;
	};
	char const* AIName;
	uint32 ScriptId;

	// helpers
	bool IsDespawnAtAction() const {
		switch (type) {
		case GAMEOBJECT_TYPE_CHEST:
			return chest.consumable;
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.consumable;
		default:
			return false;
		}
	}

	uint32 GetLockId() const {
		switch (type) {
		case GAMEOBJECT_TYPE_DOOR:
			return door.lockId;
		case GAMEOBJECT_TYPE_BUTTON:
			return button.lockId;
		case GAMEOBJECT_TYPE_QUESTGIVER:
			return questgiver.lockId;
		case GAMEOBJECT_TYPE_CHEST:
			return chest.lockId;
		case GAMEOBJECT_TYPE_TRAP:
			return trap.lockId;
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.lockId;
		case GAMEOBJECT_TYPE_AREADAMAGE:
			return areadamage.lockId;
		case GAMEOBJECT_TYPE_CAMERA:
			return camera.lockId;
		case GAMEOBJECT_TYPE_FLAGSTAND:
			return flagstand.lockId;
		case GAMEOBJECT_TYPE_FISHINGHOLE:
			return fishinghole.lockId;
		case GAMEOBJECT_TYPE_FLAGDROP:
			return flagdrop.lockId;
		default:
			return 0;
		}
	}

	bool GetDespawnPossibility() const // despawn at targeting of cast?
	{
		switch (type) {
		case GAMEOBJECT_TYPE_DOOR:
			return door.noDamageImmune;
		case GAMEOBJECT_TYPE_BUTTON:
			return button.noDamageImmune;
		case GAMEOBJECT_TYPE_QUESTGIVER:
			return questgiver.noDamageImmune;
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.noDamageImmune;
		case GAMEOBJECT_TYPE_FLAGSTAND:
			return flagstand.noDamageImmune;
		case GAMEOBJECT_TYPE_FLAGDROP:
			return flagdrop.noDamageImmune;
		default:
			return true;
		}
	}

	uint32 GetCharges() const // despawn at uses amount
	{
		switch (type) {
		//case GAMEOBJECT_TYPE_TRAP:        return trap.charges;
		case GAMEOBJECT_TYPE_GUARDPOST:
			return guardpost.charges;
		case GAMEOBJECT_TYPE_SPELLCASTER:
			return spellcaster.charges;
		default:
			return 0;
		}
	}

	uint32 GetLinkedGameObjectEntry() const {
		switch (type) {
		case GAMEOBJECT_TYPE_CHEST:
			return chest.linkedTrapId;
		case GAMEOBJECT_TYPE_SPELL_FOCUS:
			return spellFocus.linkedTrapId;
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.linkedTrapId;
		default:
			return 0;
		}
	}

	uint32 GetAutoCloseTime() const {
		uint32 autoCloseTime = 0;
		switch (type) {
		case GAMEOBJECT_TYPE_DOOR:
			autoCloseTime = door.autoCloseTime;
			break;
		case GAMEOBJECT_TYPE_BUTTON:
			autoCloseTime = button.autoCloseTime;
			break;
		case GAMEOBJECT_TYPE_TRAP:
			autoCloseTime = trap.autoCloseTime;
			break;
		case GAMEOBJECT_TYPE_GOOBER:
			autoCloseTime = goober.autoCloseTime;
			break;
		case GAMEOBJECT_TYPE_TRANSPORT:
			autoCloseTime = transport.autoCloseTime;
			break;
		case GAMEOBJECT_TYPE_AREADAMAGE:
			autoCloseTime = areadamage.autoCloseTime;
			break;
		default:
			break;
		}
		return autoCloseTime / IN_MILLISECONDS; // prior to 3.0.3, conversion was / 0x10000;
	}

	uint32 GetLootId() const {
		switch (type) {
		case GAMEOBJECT_TYPE_CHEST:
			return chest.lootId;
		case GAMEOBJECT_TYPE_FISHINGHOLE:
			return fishinghole.lootId;
		default:
			return 0;
		}
	}
	uint32 GetGossipMenuId() const {
		switch (type) {
		case GAMEOBJECT_TYPE_QUESTGIVER:
			return questgiver.gossipID;
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.gossipID;
		default:
			return 0;
		}
	}
	uint32 GetEventScriptId() const {
		switch (type) {
		case GAMEOBJECT_TYPE_GOOBER:
			return goober.eventId;
		case GAMEOBJECT_TYPE_CHEST:
			return chest.eventId;
		case GAMEOBJECT_TYPE_CAMERA:
			return camera.eventID;
		default:
			return 0;
		}
	}
};

class OPvPCapturePoint;

union GameObjectValue {
	//29 GAMEOBJECT_TYPE_CAPTURE_POINT
	struct {
		OPvPCapturePoint *OPvPObj;
	} capturePoint;
	//33 GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING
	struct {
		uint32 health;
	} building;
	struct {
        uint32 Health;
        uint32 MaxHealth;		
	} Building;	
};

// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif

struct GameObjectLocale {
	StringVector Name;
	StringVector CastBarCaption;
};

// client side GO show states
enum GOState {
	GO_STATE_ACTIVE = 0, // show in world as used and not reset (closed door open)
	GO_STATE_READY = 1, // show in world as ready (closed door close)
	GO_STATE_ACTIVE_ALTERNATIVE = 2
// show in world as used in alt way and not reset (closed door open by cannon fire)
};

#define MAX_GO_STATE              3

// from `gameobject`
struct GameObjectData {
	explicit GameObjectData() :
			dbData(true) {
	}
	uint32 id; // entry in gamobject_template
	uint16 mapid;
	uint16 phaseMask;
	float posX;
	float posY;
	float posZ;
	float orientation;
	float rotation0;
	float rotation1;
	float rotation2;
	float rotation3;
	int32 spawntimesecs;
	uint32 animprogress;
	GOState go_state;
	uint8 spawnMask;
	uint8 artKit;
	bool dbData;
};

// For containers:  [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY        -> ...
// For bobber:      GO_NOT_READY  ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
// For door(open):  [GO_NOT_READY]->GO_READY (open) ->GO_ACTIVATED (close)->GO_JUST_DEACTIVATED->GO_READY(open)  -> ...
enum LootState {
	GO_NOT_READY = 0, GO_READY, // can be ready but despawned, and then not possible activate until spawn
	GO_ACTIVATED,
	GO_JUST_DEACTIVATED
};

class Unit;

// 5 sec for bobber catch
#define FISHING_BOBBER_READY_TIME 5

class GameObject: public WorldObject, public GridObject<GameObject> {
public:
	explicit GameObject();
	~GameObject();

	void AddToWorld();
	void RemoveFromWorld();
	void CleanupsBeforeDelete(bool finalCleanup = true);

	bool Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask,
			float x, float y, float z, float ang, float rotation0,
			float rotation1, float rotation2, float rotation3,
			uint32 animprogress, GOState go_state, uint32 artKit = 0);
	void Update(uint32 p_time);
	static GameObject* GetGameObject(WorldObject& object, uint64 guid);
	GameObjectInfo const* GetGOInfo() const {
		return m_goInfo;
	}
	GameObjectData const* GetGOData() const {
		return m_goData;
	}
	GameObjectValue * GetGOValue() const {
		return m_goValue;
	}

	bool IsTransport() const;
	bool IsDynTransport() const;

	uint32 GetDBTableGUIDLow() const {
		return m_DBTableGuid;
	}

	void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f);

	void Say(int32 textId, uint32 language, uint64 TargetGuid) {
		MonsterSay(textId, language, TargetGuid);
	}
	void Yell(int32 textId, uint32 language, uint64 TargetGuid) {
		MonsterYell(textId, language, TargetGuid);
	}
	void TextEmote(int32 textId, uint64 TargetGuid) {
		MonsterTextEmote(textId, TargetGuid);
	}
	void Whisper(int32 textId, uint64 receiver) {
		MonsterWhisper(textId, receiver);
	}
	void YellToZone(int32 textId, uint32 language, uint64 TargetGuid) {
		MonsterYellToZone(textId, language, TargetGuid);
	}

	// overwrite WorldObject function for proper name localization
	const char* GetNameForLocaleIdx(LocaleConstant locale_idx) const;

	void SaveToDB();
	void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
	bool LoadFromDB(uint32 guid, Map *map);
	void DeleteFromDB();

	void SetOwnerGUID(uint64 owner) {
		// Owner already found and different than expected owner - remove object from old owner
		if (owner && GetOwnerGUID() && GetOwnerGUID() != owner) {
			ASSERT(false);
		}
		m_spawnedByDefault = false; // all object with owner is despawned after delay
		SetUInt64Value(OBJECT_FIELD_CREATED_BY, owner);
	}
	uint64 GetOwnerGUID() const {
		return GetUInt64Value(OBJECT_FIELD_CREATED_BY);
	}
	Unit* GetOwner() const;

	void SetSpellId(uint32 id) {
		m_spawnedByDefault = false; // all summoned object is despawned after delay
		m_spellId = id;
	}
	uint32 GetSpellId() const {
		return m_spellId;
	}

	time_t GetRespawnTime() const {
		return m_respawnTime;
	}
	time_t GetRespawnTimeEx() const {
		time_t now = time(NULL);
		if (m_respawnTime > now)
			return m_respawnTime;
		else
			return now;
	}

	void SetRespawnTime(int32 respawn) {
		m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
		m_respawnDelayTime = respawn > 0 ? respawn : 0;
	}
	void Respawn();
	bool isSpawned() const {
		return m_respawnDelayTime == 0
				|| (m_respawnTime > 0 && !m_spawnedByDefault)
				|| (m_respawnTime == 0 && m_spawnedByDefault);
	}
	bool isSpawnedByDefault() const {
		return m_spawnedByDefault;
	}
	void SetSpawnedByDefault(bool b) {
		m_spawnedByDefault = b;
	}
	uint32 GetRespawnDelay() const {
		return m_respawnDelayTime;
	}
	void Refresh();
	void Delete();
	void getFishLoot(Loot *loot, Player* loot_owner);
	GameobjectTypes GetGoType() const {
		return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1));
	}
	void SetGoType(GameobjectTypes type) {
		SetByteValue(GAMEOBJECT_BYTES_1, 1, type);
	}
	GOState GetGoState() const {
		return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0));
	}
	void SetGoState(GOState state) {
		SetByteValue(GAMEOBJECT_BYTES_1, 0, state);
	}
	uint8 GetGoArtKit() const {
		return GetByteValue(GAMEOBJECT_BYTES_1, 2);
	}
	void SetGoArtKit(uint8 artkit);
	uint8 GetGoAnimProgress() const {
		return GetByteValue(GAMEOBJECT_BYTES_1, 3);
	}
	void SetGoAnimProgress(uint8 animprogress) {
		SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress);
	}
	static void SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid = 0);

	void Use(Unit* user);

	LootState getLootState() const {
		return m_lootState;
	}
	void SetLootState(LootState s) {
		m_lootState = s;
	}

	uint16 GetLootMode() {
		return m_LootMode;
	}
	bool HasLootMode(uint16 lootMode) {
		return m_LootMode & lootMode;
	}
	void SetLootMode(uint16 lootMode) {
		m_LootMode = lootMode;
	}
	void AddLootMode(uint16 lootMode) {
		m_LootMode |= lootMode;
	}
	void RemoveLootMode(uint16 lootMode) {
		m_LootMode &= ~lootMode;
	}
	void ResetLootMode() {
		m_LootMode = LOOT_MODE_DEFAULT;
	}

	void AddToSkillupList(uint32 PlayerGuidLow) {
		m_SkillupList.push_back(PlayerGuidLow);
	}
	bool IsInSkillupList(uint32 PlayerGuidLow) const {
		for (std::list<uint32>::const_iterator i = m_SkillupList.begin();
				i != m_SkillupList.end(); ++i)
			if (*i == PlayerGuidLow)
				return true;
		return false;
	}
	void ClearSkillupList() {
		m_SkillupList.clear();
	}

	void AddUniqueUse(Player* player);
	void AddUse() {
		++m_usetimes;
	}

	uint32 GetUseCount() const {
		return m_usetimes;
	}
	uint32 GetUniqueUseCount() const {
		return m_unique_users.size();
	}

	void SaveRespawnTime();

	Loot loot;

	uint32 m_groupLootTimer; // (msecs)timer used for group loot
	uint32 lootingGroupLowGUID; // used to find group which is looting

	bool hasQuest(uint32 quest_id) const;
	bool hasInvolvedQuest(uint32 quest_id) const;
	bool ActivateToQuest(Player *pTarget) const;
	void UseDoorOrButton(uint32 time_to_restore = 0, bool alternative = false);
	// 0 = use `gameobject`.`spawntimesecs`
	void ResetDoorOrButton();

	void TriggeringLinkedGameObject(uint32 trapEntry, Unit* target);

	bool IsInvisibleDueToDespawn() const;
	bool isAlwaysVisibleFor(WorldObject const* seer) const;
	bool isVisibleForInState(WorldObject const* seer) const;

	uint8 getLevelForTarget(WorldObject const* target) const {
		if (Unit* owner = GetOwner())
			return owner->getLevelForTarget(target);

		return 1;
	}

	GameObject* LookupFishingHoleAround(float range);

	void CastSpell(Unit *target, uint32 spell);
	void SendCustomAnim(uint32 anim);
	bool IsInRange(float x, float y, float z, float radius) const;
	void TakenDamage(uint32 damage, Unit* who = NULL);
	void Rebuild();
	
        void ModifyHealth(int32 change, Unit* attackerOrHealer = NULL, uint32 spellId = 0);
        // sets GameObject type 33 destruction flags and optionally default health for that state
        void SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker = NULL, bool setHealth = false);
        GameObjectDestructibleState GetDestructibleState() const
        {
            if (HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED))
                return GO_DESTRUCTIBLE_DESTROYED;
            if (HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED))
                return GO_DESTRUCTIBLE_DAMAGED;
            return GO_DESTRUCTIBLE_INTACT;
        }	

	void EventInform(uint32 eventId);

	uint64 GetRotation() const {
		return m_rotation;
	}
	virtual uint32 GetScriptId() const {
		return GetGOInfo()->ScriptId;
	}
	GameObjectAI* AI() const {
		return (GameObjectAI*) m_AI;
	}

	std::string GetAIName() const;
protected:
	bool AIM_Initialize();
	uint32 m_spellId;
	time_t m_respawnTime; // (secs) time of next respawn (or despawn if GO have owner()),
	uint32 m_respawnDelayTime; // (secs) if 0 then current GO state no dependent from timer
	LootState m_lootState;
	bool m_spawnedByDefault;
	time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
						   // For traps this: spell casting cooldown, for doors/buttons: reset time.
	std::list<uint32> m_SkillupList;

	Player* m_ritualOwner; // used for GAMEOBJECT_TYPE_SUMMONING_RITUAL where GO is not summoned (no owner)
	std::set<uint32> m_unique_users;
	uint32 m_usetimes;

	typedef std::map<uint32, uint64> ChairSlotAndUser;
	ChairSlotAndUser ChairListSlots;

	uint32 m_DBTableGuid; ///< For new or temporary gameobjects is 0 for saved it is lowguid
	GameObjectInfo const* m_goInfo;
	GameObjectData const* m_goData;
	GameObjectValue * const m_goValue;

	uint64 m_rotation;

	uint16 m_LootMode; // bitmask, default LOOT_MODE_DEFAULT, determines what loot will be lootable
private:
	void RemoveFromOwner();
	void SwitchDoorOrButton(bool activate, bool alternative = false);
	GameObjectAI* m_AI;
};
#endif
